#pragma once

#include <list>
#include <mutex>
#include <condition_variable>

#include "Util.hpp"
#include "online.hpp"
#include "room.hpp"
#include "../rpc_client/Rpc_client.hpp"
#include "../rpc_common/Util.hpp"

template <class T>
class match_queue
{
public:
    // 获取元素个数
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    // 判断是否为空
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex); // todo:读写锁优化
        return _list.empty();
    }
    // 阻塞线程
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    // 入队数据，并唤醒线程
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    // 出队数据
    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty())
        {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }
    // 移除指定的数据
    void remove(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }

private:
    std::list<T> _list;            // 用链表而不直接使用queue是因为有中间删除数据的需要（玩家取消匹配）
    std::condition_variable _cond; // 这个条件变量主要为了阻塞消费者，后边使用的时候：队列中元素个数<2则阻塞
    std::mutex _mutex;
};

class matcher
{
public:
    matcher(room_manager *rm, online_manager *om, Rpc::Client::RpcClient::ptr clientp) : _rm(rm), _om(om), _th_normal(std::thread(std::bind(&matcher::th_normal_entry, this))),
                                                                                         _th_high(std::thread(std::bind(&matcher::th_high_entry, this))),
                                                                                         _clientp(clientp)
    {
        ILOG("matchaer init success!");
    }
    bool add(uint64_t uid)
    {
        // 根据玩家的score，添加到不同的匹配队列
        //  根据用户ID，获取玩家信息
        Json::Value id, user;
        id["uid"] = uid;
        if (!_clientp->call("select_by_id", id, user))
        {
            ELOG("matcher: rpc call select_by_id failed!");
            return false;
        }
        if (user["result"] == false)
        {
            std::string err_str = user["reason"].asString();
            ELOG("matcher: get user:%ld info failed: %s", uid, err_str.c_str());
            return false;
        }
        int score = user["score"].asInt();

        if (score < 2000)
        {
            _q_normal.push(uid);
        }
        else if (score >= 2000)
        {
            _q_high.push(uid);
        }
        else
        {
        }

        return true;
    }
    bool del(uint64_t uid)
    {
        Json::Value id, user;
        id["uid"] = uid;
        if (!_clientp->call("select_by_id", id, user))
        {
            ELOG("matcher: rpc call select_by_id failed!");
            return false;
        }
        if (user["result"] == false)
        {
            std::string err_str = user["reason"].asString();
            ELOG("matcher: get user:%ld info failed: %s", uid, err_str.c_str());
            return false;
        }

        int score = user["score"].asInt();
        if (score < 2000)
        {
            _q_normal.remove(uid);
        }
        else if (score >= 2000)
        {
            _q_high.remove(uid);
        }
        else
        {
        }

        return true;
    }
    void handle_match(match_queue<uint64_t> &mq)
    {
        while (1)
        {
            // 判断队列人数是否>=2，<2则阻塞等待
            while (mq.size() < 2)
            {
                mq.wait();
            }
            // 人数足够，出队两个玩家
            uint64_t uid1, uid2;
            bool ret = mq.pop(uid1);
            if (ret == false)
            {
                continue;
            }
            ret = mq.pop(uid2);
            if (ret == false)
            {
                this->add(uid1); // uid2出队失败需要将uid1重新加入匹配队列
                continue;
            }
            // 校验两个玩家是否在线，如果有人掉线，则要将另一个人重新加入队列
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if (conn1.get() == nullptr)
            {
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if (conn2.get() == nullptr)
            {
                this->add(uid1);
                continue;
            }
            // 为两个玩家创建房间，并将玩家加入房间中
            room_ptr rp = _rm->create_room(uid1, uid2, false);
            if (rp.get() == nullptr)
            {
                // 如果房间创建失败，重新加入匹配队列
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            // 对两个玩家进行响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void th_normal_entry() { return handle_match(_q_normal); }
    void th_high_entry() { return handle_match(_q_high); }

private:
    match_queue<uint64_t> _q_normal; // 普通选手匹配队列(score < 2000)
    match_queue<uint64_t> _q_high;   // 高手匹配队列(score >= 2000)
    std::thread _th_normal;          // 对应两个匹配队列的处理线程
    std::thread _th_high;
    std::thread _th_weiqi;
    room_manager *_rm;
    online_manager *_om;
    Rpc::Client::RpcClient::ptr _clientp;
};